package cn.js189.uqc.util;

import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.UrlEncoded;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class EInvoiceUtil {

	private static final Logger LOGGER = LoggerFactory.getLogger(EInvoiceUtil.class);

	private EInvoiceUtil() {
		throw new IllegalStateException("Utility class");
	}

	public static final String CONTENT_TYPE = "Content-Type";

	private static final String PARAM_A = "&lanId=";

	private static final String PARAM_B = "&remoteSource=";

	private static final String PARAM_C = "&remotepass=";

	/**
	 * 电子发票接口basePath
	 */
	private static final String URL =  "http://jsjteop.telecomjs.com:8764/jseop/billing_zw/";
	
	public static final String REMOTE_SOURCE = "80000";
	
	//线下营销活动服务预约basePath
	private static final String URL2 =  "http://openapi.telecomjs.com:80/eop/ZHYX/";

	/**
	 * DCOSS请求头中的 X-APP-ID
	 */
	public static final String APP_KEY = "b8c2e5dcfe54af717e669739e5790478";//生产
	public static final String TEST_APP_KEY = "b8953ac92c492693b0ed3bbffabda9a1";//测试
	public static final String APP_KEY_KF = "a428c500e545b21142cf71385e551eac";//客服系统-万号、小A
	/**
	 * DCOSS请求头中的 X-APP-KEY
	 */
	public static final String APP_SECRET = "467a04de96967045d75f8d06bec20e8c";//生产
	public static final String TEST_APP_SECRET = "335682dc276f3cda394e94327da0b015";//测试
	public static final String APP_SECRET_KF = "c254a5b589312a9713b1cbfcb86eeb11";
	/**
     * 新EOP请求头中的 X-APP-ID
     */
    public static final String EOP_APP_KEY = "944d8bf998dd426e8b198de2f07c9c80";

    /**
     * 新EOP请求头中的 X-APP-KEY
     */
    public static final String EOP_APP_SECRET = "8fc30c17998f4b609fd1ce814b9f2963";

	/**
	 * remotepass加密key
	 */
	private static final String RESOURCE_KEY = "RR23456789abcdFF254567pP";

	/**
	 * remotepass加密向量iv
	 */
	private static final String RESOURCE_KEYIV = "65432187";

	/**
	 * remotepass需要对此数据进行加密
	 */
	private static final String CIPHER = "I60REB#F!";

	private static String remotePass;


	private static final Set<String> resourceAddrSet = new HashSet<>(6);

	private static final Map<String, String> headMap = new HashMap<>(4);

	private static final Map<String, String> familyIdMap = new HashMap<>(3);

	private static Map<String, String> errmsgMap;

	static {

		resourceAddrSet.add("monthlyBillJS");     // 月结类型的增值税普通电子发票
		resourceAddrSet.add("paymentJS");         // 实缴类型的增值税普通电子发票
		resourceAddrSet.add("orderOneItemJS");    // 营业订单类型的增值税普通电子发票
		resourceAddrSet.add("issueInvoiceJS");    // 开具发票
		resourceAddrSet.add("invoiceJS");         // iAreaBill查询发票
		resourceAddrSet.add("hisInvoiceInfoJS");  // 查询开票历史

		familyIdMap.put("0", "100000002");    // 固话
		familyIdMap.put("2", "100000379");    // 手机
		familyIdMap.put("3", "100000009");    // 宽带

		// 自定义请求头
		headMap.put("X-APP-ID", APP_KEY);
		headMap.put("X-APP-KEY", APP_SECRET);

		// // 电子票据已开具，请重新查询
		// errmsgMap.put("681870101", "电子发票已开具，请在开票历史中查询");
		// // 客户是一般纳税人
		// errmsgMap.put("61830363", "政企客户您好，请联系您的专属客户经理开具发票");
		// errmsgMap.put("61830364", "该类型充值（如充值卡充值、第三方充值等）请联系第三方充值或购卡渠道开具电子发票");
		// errmsgMap.put("61830368", "您本期月结发票可开票金额为零，无需开具发票");
		// errmsgMap.put("681870108", "您本期月结发票可开票金额为零，无需开具发票");
		// // 打印电子发票时预存的金额已经发生变化
		// errmsgMap.put("681870110", "您预存的金额已抵扣通讯费用，可开具月结发票");
		// // 该充值不允许开具电子发票
		// errmsgMap.put("681870111", "该类型充值（如充值卡充值、第三方充值等）请联系第三方充值或购卡渠道开具电子发票");
		// // 没有可以打印的账目
		// errmsgMap.put("681870113", "电子发票已开具，请在开票历史中查询");
		// // 预存余额已经发生消费请开具月结发票
		// errmsgMap.put("677514101504", "您预存的金额已抵扣通讯费用，可开具月结发票");
		//
		// errmsgMap.put("-10005", "查询购物车数据异常");
		// // 该号码没有费用信息哦，请重新查询～
		// errmsgMap.put("-10001", "亲，暂无记录哦~");
		// errmsgMap.put("-10007", "查询购物车数据逻辑异常");

		try {
			remotePass = Des3.des3EncodeCBC(RESOURCE_KEY, RESOURCE_KEYIV, CIPHER);
		} catch (Exception e) {
			LOGGER.debug("REMOTEPASS 初始化失败!{}", e.getMessage());
		}

	}
	
	public static void needInit() {
		errmsgMap = null;
	}

	public static boolean checkErrmsgMap() {
		return errmsgMap == null;
	}

	public static void initErrmsgMap() {
		errmsgMap = new HashMap<>();
	}

	public static String getErrmsg(String errcode) {
		return errmsgMap.get(errcode);
	}

	public static void cleanErrmsg() {
		errmsgMap.clear();
	}

	public static void setErrmsgMap(String errorCode, String errMsg) {
		errmsgMap.put(errorCode, errMsg);
	}


	public static String getRemotePass() {
		return remotePass;
	}

	public static String getRemoteUrl(String resourceAddr, String serialNumber, String areaCode, String familyId, String beginTime, String endTime) {

		StringBuilder sb = new StringBuilder(URL);
		String landId = StaticDataMappingUtil.getRegionByAreaCode(areaCode);

		sb.append(resourceAddr).append("/").append(resourceAddr).append("?serialNumber=").append(serialNumber);
		if (StringUtils.equals(resourceAddr, "orderOneItemJS")) {
			String productId = familyIdMap.get(familyId);
			sb.append("&productId=").append(productId);
		}
		sb.append(PARAM_A).append(landId).append("&familyId=").append(familyId).append("&beginTime=").append(beginTime)
				.append("&endTime=").append(endTime).append(PARAM_B).append(REMOTE_SOURCE).append(PARAM_C).append(remotePass);

		return sb.toString();
	}

	public static boolean containResourceAddr(String resourceAddr) {
		return resourceAddrSet.contains(resourceAddr.trim());
	}

	public static String setHeadMap(String areaCode) {
		String landId = StaticDataMappingUtil.getRegionByAreaCode(areaCode);
		String uuid = UUID.randomUUID().toString();
		headMap.put("X-CTG-Region-ID", landId);
		headMap.put("X-CTG-Request-ID", uuid);
		return uuid;
	}

	public static Map<String,String> getInvoiceHeadMap() {
		return headMap;
	}

	public static String getJsRemoteUrl(String resourceAddr, String invoiceId, String areaCode) {
		StringBuilder sb = new StringBuilder(URL);
		String landId = StaticDataMappingUtil.getRegionByAreaCode(areaCode);
		sb.append(resourceAddr).append("/").append(resourceAddr).append("?").append("invoiceId=").append(invoiceId).append(PARAM_A)
				.append(landId).append(PARAM_B).append(REMOTE_SOURCE).append(PARAM_C).append(remotePass);
		return sb.toString();
	}

	public static String getRemoteUrlHistory(String resourceAddr, String type, String serialNumber, String areaCode, String familyId, String beginTime, String endTime) {
		StringBuilder sb = new StringBuilder(URL);
		String landId = StaticDataMappingUtil.getRegionByAreaCode(areaCode);
		String productId = familyIdMap.get(familyId);
		sb.append(resourceAddr).append("/").append(resourceAddr).append("?").append("serialNumber=").append(serialNumber);
		sb.append("&productId=").append(productId);
		sb.append(PARAM_A).append(landId).append("&familyId=").append(familyId).append("&type=").append(type).append("&beginTime=")
				.append(beginTime).append("&endTime=").append(endTime).append(PARAM_B).append(REMOTE_SOURCE).append(PARAM_C).append(remotePass);
		return sb.toString();
	}

	public static String getPostUrl(String resourceAddr) {
		StringBuilder sb = new StringBuilder(URL);
		sb.append(resourceAddr).append("/").append(resourceAddr).append("?").append("remoteSource=").append(REMOTE_SOURCE).append(PARAM_C).append(remotePass);
		return sb.toString();
	}
	
	public static String getActivityInfoRemoteUrl(String resourceAddr, String activityId) {
		StringBuilder sb = new StringBuilder(URL2);
		sb.append(resourceAddr).append("/").append(resourceAddr).append("?").append("activityId=").append(activityId);
		return sb.toString();
	}
	
	public static String getQueryOssBuildingListUrl(String resourceAddr, String gridId,String fullName) {
		StringBuilder sb = new StringBuilder(URL2);
		sb.append(resourceAddr).append("/").append(resourceAddr).append("?").append("gridId=").append(gridId);
		if(!StringUtils.isEmpty(fullName)){
			sb.append("&fullName=").append(fullName);
		}
		return sb.toString();
	}
	
	
	public static String getQueryOssDoorsUrl(String resourceAddr, String buildingId,String cellX,String fullName) {
		StringBuilder sb = new StringBuilder(URL2);
		sb.append(resourceAddr).append("/").append(resourceAddr).append("?").
		append("buildingId=").append(buildingId).append("&cellX=").append(cellX);
		if(!StringUtils.isEmpty(fullName)){
			sb.append("&fullName=").append(fullName);
		}
		return sb.toString();
	}
	/**
	 * @param url     地址
	 * @param param   参数
	 * @param isproxy 代理
	 * @param headMap 请求头塞值
	 * @return 返回值
	 */
	public static String sendRequestWithHead(String url, String param, boolean isproxy, String requestMethod, Map<String, String> headMap) {
		LOGGER.debug("请求URL: {} isproxy {}", url,isproxy);
		BufferedReader in = null;
		StringBuilder sb = new StringBuilder();
		HttpURLConnection conn = null;
		try {
			URL realUrl = new URL(url);
			conn = (HttpURLConnection) realUrl.openConnection();

			// 发送POST请求必须设置如下两行
			conn.setDoOutput(true);
			conn.setDoInput(true);
			// 请求方式 POST / GET
			conn.setRequestMethod(requestMethod);

			// 设置通用的请求属性
			for (Map.Entry<String, String> entry : setProperty(headMap).entrySet()) {
				conn.setRequestProperty(entry.getKey(), entry.getValue());
			}
			conn.connect();

		} catch (IOException e) {
			LOGGER.debug("发送请求出现异常！{}", e.getMessage());
		}

		if (null != conn) {
			// 获取URLConnection对象对应的输出流
			try (OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8)) {
				// 发送请求参数
				out.write(param);

				// 定义BufferedReader输入流来读取URL的响应
				if (conn.getResponseCode() == HttpURLConnection.HTTP_OK
						|| conn.getResponseCode() == HttpURLConnection.HTTP_CREATED
						|| conn.getResponseCode() == HttpURLConnection.HTTP_ACCEPTED) {

					in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
				} else {
					in = new BufferedReader(new InputStreamReader(conn.getErrorStream(), StandardCharsets.UTF_8));
				}

				String line;
				while ((line = in.readLine()) != null) {
					sb.append(line);
				}
				in.close();

			} catch (IOException e) {
				LOGGER.debug("发送请求出现异常！{}", e.getMessage());
			}
		}

		return sb.toString();
	}

	//设置请求头属性
	public static Map<String, String> setProperty(Map<String, String> map) {

		HashMap<String, String> pMap = new HashMap<>();

		if (null != map && !map.isEmpty()) {
			for (Map.Entry<String, String> entry : map.entrySet()) {
				pMap.put(entry.getKey(), entry.getValue());
			}
		}

		// pMap.put("Accept-Encoding", "gzip"); //请求定义gzip,响应也是压缩包
		pMap.put("connection", "Keep-Alive");
		pMap.put("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
		pMap.put(CONTENT_TYPE, "application/json;charset=utf-8");
		return pMap;
	}

	/**
	 * 获取 invoice_url 中的s_pappid
	 */
	public static MultiMap<String> urlParamResolver(String invoiceUrl) {

		MultiMap<String> values = new MultiMap<>();
		UrlEncoded.decodeTo(invoiceUrl, values, StandardCharsets.UTF_8);
		LOGGER.info("解析invoice_url的参数 :{}", values);

		return values;
	}

	public static String postJSONHttpProxy(String url, String param, int timeout, String switchStr) {
		LOGGER.debug("postJSONHttpProxy switchStr{}",switchStr);
		HttpClientBuilder builder = HttpClientBuilder.create();
		try(CloseableHttpClient client = builder.build()) {
			HttpPost post = new HttpPost(url);
			//4.3之后，超时时间用此方法设置，早期方法会报过时
			RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout).build();
			post.setConfig(requestConfig);
			post.setHeader(CONTENT_TYPE, "Content-Type:application/json;charset=utf-8");
			StringEntity entity = new StringEntity(param, StandardCharsets.UTF_8);
			entity.setContentType("application/json;charset=utf-8");
			post.setEntity(entity);
			HttpResponse response = client.execute(post);
			if (response.getStatusLine().getStatusCode() == 200) {
				return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
			}
		} catch (Exception e) {
			LOGGER.debug(e.getMessage());
		}
		return null;
	}


	/**
	 * 上传文件至url
	 * @param url
	 * @param filePath
	 * @return
	 * @throws IOException
	 */
	public static String uploadFile(String url, String filePath, boolean isproxy) throws IOException {
		LOGGER.debug("uploadFile isproxy {}",isproxy);
		String result = "";
		HttpURLConnection con;
		try {
			File file = new File(filePath);
			if (!file.exists() || !file.isFile()) {
				throw new IOException("文件不存在");
			}
			URL urlObj = new URL(url);
			con = (HttpURLConnection) urlObj.openConnection();

			con.setRequestMethod("POST"); // 设置关键值,以Post方式提交表单，默认get方式
			con.setDoInput(true);
			con.setDoOutput(true);
			con.setUseCaches(false); // post方式不能使用缓存
			// 设置请求头信息
			con.setRequestProperty("Connection", "Keep-Alive");
			con.setRequestProperty("Charset", "UTF-8");
			// 设置边界
			String boundary = "----------" + System.currentTimeMillis();
			con.setRequestProperty(CONTENT_TYPE, "multipart/form-data; boundary=" + boundary);
			// 请求正文信息
			// 第一部分：
			StringBuilder sb = new StringBuilder();
			sb.append("--"); // 必须多两道线
			sb.append(boundary);
			sb.append("\r\n");
			sb.append("Content-Disposition: form-data;name=\"pdf\";filelength=\"" + file.length() + "\";filename=\"" + file.getName() + "\"\r\n");
			sb.append("Content-Type:application/octet-stream\r\n\r\n");
			byte[] head = sb.toString().getBytes(StandardCharsets.UTF_8);
			// 获得输出流
			OutputStream out = new DataOutputStream(con.getOutputStream());
			// 输出表头
			out.write(head);
			// 文件正文部分
			// 把文件已流文件的方式 推入到url中
			try(DataInputStream in = new DataInputStream(new FileInputStream(file))){
				int bytes = 0;
				byte[] bufferOut = new byte[1024];
				while ((bytes = in.read(bufferOut)) != -1) {
					out.write(bufferOut, 0, bytes);
				}
			}
			// 结尾部分
			byte[] foot = ("\r\n--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8);// 定义最后数据分隔线
			out.write(foot);
			out.flush();
			out.close();

		} catch (Exception e) {
			return null;
		}

		// 定义BufferedReader输入流来读取URL的响应
		StringBuilder builder = new StringBuilder();
		try (BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));){

			String line = null;
			while ((line = reader.readLine()) != null) {
				builder.append(line);
			}
			result = builder.toString();
		} catch (IOException e) {
			throw new IOException("数据读取异常");
		}
		return result;

	}

	/**
	 * 从url下载文件到本地
	 * @param urlStr
	 * @param fileName
	 * @param savePath
	 * @throws IOException
	 */
	public static String downLoadFromUrl(String urlStr, String fileName, String savePath) throws IOException {
		URL url = new URL(urlStr);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		// 设置超时间为3秒
		conn.setConnectTimeout(3 * 1000);
		// 防止屏蔽程序抓取而返回403错误
		conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
		// 得到输入流
		InputStream inputStream = conn.getInputStream();
		// 获取自己数组
		byte[] getData = readInputStream(inputStream);
		// 文件保存位置
		File saveDir = new File(savePath);
		if (!saveDir.exists()) {
			saveDir.mkdirs();
		}

		File file = new File(saveDir + File.separator + fileName);
		try(FileOutputStream fos = new FileOutputStream(file)){
			fos.write(getData);
		}

		inputStream.close();

		LOGGER.info("info: {} download success", urlStr);

		return savePath + fileName;

	}

	/**
	 * 从输入流中获取字节数组
	 *
	 * @param inputStream
	 * @return
	 * @throws IOException
	 */
	public static byte[] readInputStream(InputStream inputStream) throws IOException {
		byte[] buffer = new byte[1024];
		int len = 0;
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		while ((len = inputStream.read(buffer)) != -1) {
			bos.write(buffer, 0, len);
		}
		bos.close();
		return bos.toByteArray();
	}

	/**
	 * 判断字符串是否是全数字
	 */
	public static boolean isNumeric(String numStr) {
		Pattern pattern = Pattern.compile("[0-9]*");
		Matcher isNum = pattern.matcher(numStr);
		return isNum.matches();
	}


}
